package cn.virens.database.common;

import java.util.function.Consumer;
import java.util.function.Function;

import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import cn.virens.exception.APIException;

public class TransactionExecute {
	private PlatformTransactionManager transactionManager;

	public TransactionExecute(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public void readTransaction(Consumer<TransactionStatus> consumer) {
		readTransaction((s) -> {
			consumer.accept(s);

			return null;
		});
	}

	public <T> T readTransaction(Function<TransactionStatus, T> function) {
		T result;

		DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
		definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		definition.setReadOnly(true);

		TransactionStatus status = transactionManager.getTransaction(definition);
		try {
			result = function.apply(status);

			transactionManager.commit(status);
		} catch (APIException ex) {
			transactionManager.rollback(status);

			throw new APIException(ex);
		} catch (Throwable ex) {
			transactionManager.rollback(status);

			throw new APIException(ex);
		}

		return result;
	}

	public void writeTransaction(Consumer<TransactionStatus> consumer) {
		writeTransaction((s) -> {
			consumer.accept(s);

			return null;
		});
	}

	/**
	 * 创建独立写事物
	 */
	public void writeTransaction0(Consumer<TransactionStatus> function) {
		DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
		definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
		definition.setReadOnly(false);

		TransactionStatus status = transactionManager.getTransaction(definition);
		try {
			function.accept(status);

			transactionManager.commit(status);
		} catch (APIException ex) {
			transactionManager.rollback(status);

			throw new APIException(ex);
		} catch (Throwable ex) {
			transactionManager.rollback(status);

			throw new APIException(ex);
		}
	}

	public <T> T writeTransaction(Function<TransactionStatus, T> function) {
		T result;

		DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
		definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		definition.setReadOnly(false);

		TransactionStatus status = transactionManager.getTransaction(definition);
		try {
			result = function.apply(status);

			transactionManager.commit(status);
		} catch (APIException ex) {
			transactionManager.rollback(status);

			throw new APIException(ex);
		} catch (Throwable ex) {
			transactionManager.rollback(status);

			throw new APIException(ex);
		}

		return result;
	}

}
